﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using ERTesting.Dao;
using ERTesting.Model.Result;
using GalaSoft.MvvmLight;

namespace ERTesting.Gui.ViewModel.Config
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm/getstarted
    /// </para>
    /// </summary>
    public class ScenarioResultViewModel : ViewModelBase, IEquatable<ScenarioResultViewModel>
    {

#region Private Members

        private ITestResultService _resultService;
#endregion Private Members
        #region Public Properties


        public string DisplayName
        {
            get { return String.Format("{0: dd/MM/yy HH:mm} - {1}", ScenarioResult.TimeStamp,ScenarioResult.IsError?"Error":ScenarioResult.IsPass?"Passed":"Failed"); }
        }

        /// <summary>
        /// The <see cref="Baseline" /> property's name.
        /// </summary>
        public const string BasslinePropertyName = "Baseline";

        private ScenarioResultViewModel _baseline = null;

        /// <summary>
        /// Gets the Bassline property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ScenarioResultViewModel Baseline
        {
            get
            {
                if(_baseline == null && !String.IsNullOrEmpty(ScenarioResult.BaselineId))
                {
                    _resultService.GetDefaultBaselineResult(ScenarioResult, (baseline, ex) =>
                                                                                {
                                                                                    if(baseline != null && ScenarioResult.Id != baseline.Id)
                                                                                    {
                                                                                        Baseline
                                                                                            =
                                                                                            ViewModelLocator.Instance.GetViewModel<ScenarioResultViewModel>("scenarioResult", baseline);    
                                                                                    }
                                                                                    
                                                                                });
                }
                return _baseline;
            }

            set
            {
                if (_baseline == value)
                {
                    return;
                }
                _baseline = value;

                UpdateBaseline(value);

                // Update bindings, no broadcast
                RaisePropertyChanged(BasslinePropertyName);

            }
        }

        private void UpdateBaseline(ScenarioResultViewModel baseline)
        {
            ObservableCollection<TestOutputViewModel> baselineOutputs = baseline.Outputs;

            Dictionary<string, TestOutputViewModel> dictionary = baselineOutputs.ToDictionary(b => b.Range, b => b);

            ScenarioResult.BaselineId = baseline.ScenarioResult.Id;

            foreach (TestOutputViewModel output in Outputs)
            {
                output.ExpectedValue = dictionary[output.Range].ActualValue;
                if(dictionary[output.Range].SignOff == SignOffEnum.IgnoreOutput)
                {
                    output.SignOff = SignOffEnum.IgnoreOutput;
                }
            }
            ViewModelLocator viewModelLocator = ViewModelLocator.Instance;
            Outputs = new ObservableCollection<TestOutputViewModel>(
                from o in ScenarioResult.Outputs select viewModelLocator.GetViewModel<TestOutputViewModel>("output", o));

            
        }

        /// <summary>
        /// The <see cref="Outputs" /> property's name.
        /// </summary>
        public const string OutputsPropertyName = "Outputs";

        private ObservableCollection<TestOutputViewModel> _outputs = new ObservableCollection<TestOutputViewModel>();
        public ScenarioResult ScenarioResult { get; private set; }

        /// <summary>
        /// Gets the Outputs property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ObservableCollection<TestOutputViewModel> Outputs
        {
            get
            {
                if(!ScenarioResult.Initialised)
                {
                    _resultService.GetOutputsForResult(ScenarioResult, (outputs, ex)=>
                                                                           {
                                                                               if (outputs == null)
                                                                               {
                                                                                   throw new ApplicationException("The outputs for the result could not be loaded.",ex);
                                                                               }
                                                                               
                                                                                   ScenarioResult.Initialise(outputs);
                                                                                   ViewModelLocator viewModelLocator = ViewModelLocator.Instance;
                                                                                   Outputs = new ObservableCollection<TestOutputViewModel>(
                                                                                       from o in ScenarioResult.Outputs select viewModelLocator.GetViewModel<TestOutputViewModel>("output", o));

                                                                               Baseline = Baseline;


                                                                           });
                }
                return _outputs;
            }

            set
            {
                if (_outputs == value)
                {
                    return;
                }

                _outputs = value;

                // Update bindings, no broadcast
                RaisePropertyChanged(OutputsPropertyName);
            }
        }

        #endregion Public Properties

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the ScenarioResultViewModel class.
        /// </summary>
        public ScenarioResultViewModel(ScenarioResult scenarioResult, ITestResultService resultService)
        {
            ScenarioResult = scenarioResult;
            _resultService = resultService;
            if (scenarioResult.Initialised)
            {
                ViewModelLocator viewModelLocator = ViewModelLocator.Instance;
                Outputs = new ObservableCollection<TestOutputViewModel>(
                    from o in ScenarioResult.Outputs select viewModelLocator.GetViewModel<TestOutputViewModel>("output", o));

                Baseline = Baseline;
            }
        }

        #endregion Constructors

        #region public Methods

        public bool Equals(ScenarioResultViewModel other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Equals(other.ScenarioResult, ScenarioResult);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (ScenarioResultViewModel)) return false;
            return Equals((ScenarioResultViewModel) obj);
        }

        public override int GetHashCode()
        {
            return (ScenarioResult != null ? ScenarioResult.GetHashCode() : 0);
        }

        public static bool operator ==(ScenarioResultViewModel left, ScenarioResultViewModel right)
        {
            return Equals(left, right);
        }

        public static bool operator !=(ScenarioResultViewModel left, ScenarioResultViewModel right)
        {
            return !Equals(left, right);
        }

        #endregion public Methods
    }
}